/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#ifndef __LLDP_PARSE__
#define __LLDP_PARSE__

#include <cstdint>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
#include <mc/dict.h>

namespace dev {

// 常量定义
const uint32_t    MANUFACTURE_ID            = 0x000007DB;
const uint8_t     RSP_CMD_TYPE              = 0x00;
const uint8_t     RSP_SUB_CMD               = 0x00;
const uint32_t    LLDPDU_TLC_PACKET_MAX_LEN = 1500;
const uint32_t    LLDP_MAX_TLV_NUM          = 64;
const uint32_t    TLV_HEADER_LEN            = 2;
const std::string org_oui_byte("\x00\x80\xC2", 3);
#define LLDP_HEADER_LEN 22
#define LLDP_TLV_TYPE_RESERVED_MIN 9
#define LLDP_TLV_TYPE_RESERVED_MAX 126

// 响应头结构
struct vdpci_lldp_response {
    uint32_t manufacture_id;
    uint8_t  cmd_type;
    uint8_t  sub_cmd;
    uint8_t  rsv;
    uint8_t  port_id;
    uint8_t  dest_addr[6];
    uint8_t  src_addr[6];
    uint16_t ether_type;
    uint8_t  data[];
};

// TLV 头部（2 字节）：高 1bit 是长度高位，低 7bit 为类型；另 1 字节为长度低位
struct tlv_header {
    uint8_t info_string_high; // 高 1bit: 长度高位，低 7bit: 类型高位
    uint8_t info_string_low;  // 长度低位
};

struct tlv_info {
    uint8_t     type;
    uint16_t    info_string_len;
    std::string info_string;
};

// ============================================================================
// 枚举定义
// ============================================================================

enum class tlv_type : uint8_t {
    CHASSIS_ID_TLV   = 1,
    PORT_ID_TLV      = 2,
    TTL_TLV          = 3,
    SYSTEM_NAME_TLV  = 5,
    ORG_SPECIFIC_TLV = 127
};

enum class chassis_id_subtype : uint8_t {
    CHASSIS_COMPONENT = 1,
    INTERFACE_ALIAS   = 2,
    PORT_COMPONTENT   = 3,
    MAC_ADDRESS       = 4,
    NETWORK_ADDRESS   = 5,
    INTERFACE_NAME    = 6,
    LOCALLY_ASSIGNED  = 7
};

enum class port_id_subtype : uint8_t {
    INTERFACE_ALIAS  = 1,
    PORT_COMPONTENT  = 2,
    MAC_ADDRESS      = 3,
    NETWORK_ADDRESS  = 4,
    INTERFACE_NAME   = 5,
    AGENT_CIRCUIT_ID = 6,
    LOCALLY_ASSIGNED = 7
};

enum class IEEE_subtype : uint8_t {
    PORT_VLAN_ID              = 1,
    PORT_AND_PROTOCOL_VLAN_ID = 2,
    VLAN_NAME                 = 3,
    PROTOCOL_IDENTITY         = 4,
    VID_USAGE_DIGEST          = 5,
    MANAGEMENT_VID            = 6,
    LINK_AGGREGATION          = 7
};

// ============================================================================
// 结构体定义
// ============================================================================

struct tlv_length_range {
    uint16_t min;
    uint16_t max;
};

struct lldp_tlv {
    tlv_type    type;
    std::string tlv_info_string;
};

struct chassis_id_data {
    std::string chassis_id;
    std::string chassis_id_subtype;
};

struct port_id_data {
    std::string port_id;
    std::string port_id_subtype;
};

struct ttl_data {
    uint16_t ttl;
};

struct system_name_data {
    std::string system_name;
};

struct org_specific_data {
    std::optional<uint16_t> vlan_id;
};

// ============================================================================
// 常量映射表
// ============================================================================

// TLV 类型到长度范围的映射（C++17 使用 inline 避免 ODR）
inline const std::unordered_map<uint8_t, tlv_length_range> tlv_type_tlv_length_map{
    {0, {0, 0}},   {1, {2, 256}}, {2, {2, 256}}, {3, {2, 2}},   {4, {0, 255}},
    {5, {0, 255}}, {6, {0, 255}}, {7, {4, 4}},   {8, {9, 167}}, {127, {4, 511}}
};

inline const std::unordered_map<chassis_id_subtype, std::string> chassis_id_name_switch = {
    {chassis_id_subtype::CHASSIS_COMPONENT, "ChassisComp"},
    {chassis_id_subtype::INTERFACE_ALIAS, "IfAlias"},
    {chassis_id_subtype::PORT_COMPONTENT, "PortComp"},
    {chassis_id_subtype::MAC_ADDRESS, "MacAddr"},
    {chassis_id_subtype::NETWORK_ADDRESS, "NetworkAddr"},
    {chassis_id_subtype::INTERFACE_NAME, "IfName"},
    {chassis_id_subtype::LOCALLY_ASSIGNED, "LocalAssign"},
};

inline const std::unordered_map<port_id_subtype, std::string> port_id_name_switch = {
    {port_id_subtype::INTERFACE_ALIAS, "IfAlias"},
    {port_id_subtype::PORT_COMPONTENT, "PortComp"},
    {port_id_subtype::MAC_ADDRESS, "MacAddr"},
    {port_id_subtype::NETWORK_ADDRESS, "NetworkAddr"},
    {port_id_subtype::INTERFACE_NAME, "IfName"},
    {port_id_subtype::AGENT_CIRCUIT_ID, "AgentId"},
    {port_id_subtype::LOCALLY_ASSIGNED, "LocalAssign"},
};

// ============================================================================
// LLDP 类定义
// ============================================================================

class MC_API lldp {
public:
    // 对外接口：解析完整报文
    mc::dict parse(const std::vector<uint8_t>& rsp_bin) const;

    // 对外接口：解析单个TLV，返回包含所有解析字段的字典
    mc::dict parse_lldp_tlv(const lldp_tlv& tlv) const;

private:
    void validate_rsp_data(const vdpci_lldp_response& rsp) const;
    bool check_tlv_type_and_length(uint8_t tlv_value, uint16_t tlv_length) const;
    std::vector<tlv_info> parse_tlv_list(const std::vector<uint8_t>& rsp_bin, size_t data_size) const;
    mc::dict build_parse_result(const vdpci_lldp_response* rsp, const std::vector<tlv_info>& tlvs) const;

    std::optional<chassis_id_data> parse_chassis_id_tlv(const std::string& info_string) const;
    std::optional<port_id_data>    parse_port_id_tlv(const std::string& info_string) const;
    std::optional<ttl_data>        parse_ttl_tlv(const std::string& info_string) const;
    std::optional<system_name_data>
    parse_system_name_data_tlv(const std::string& info_string) const;
    std::optional<org_specific_data>
    parse_org_specific_data_tlv(const std::string& info_string) const;
};

} // namespace dev

#endif // __LLDP_PARSE__